﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.Practices.ServiceLocation;
using Newtonsoft.Json;
using Guitar.Comet.Customer;
using Guitar.Comet.Message;
using Timer = System.Timers.Timer;

namespace Guitar.Comet.Client.SignalR
{
    /// <summary>
    /// SignalR 实现长连接 客户端
    /// </summary>
    public class SignalRClient : ISignalRClient
    {
        private static HubConnection _hubConn;
        private static IHubProxy _hubProxy;
        private static bool _isInit = false;
        private static readonly object LockObject = new object();
        private static readonly Dictionary<string, ICustomer> _subscriberInstance = new Dictionary<string, ICustomer>();
        private static readonly Timer Timer = new Timer(2 * 1000);

        /// <summary>
        /// 线程安全
        /// </summary>
        private void CreateProxy()
        {
            if (_isInit) return;

            lock (LockObject)
            {
                if (_isInit) return;

                if (_hubConn == null || _hubConn.State == ConnectionState.Disconnected)
                {
                    _hubConn = new HubConnection(SignalRConfiguration.ServiceUrl,
                        new Dictionary<string, string>() { { "ClientName", SignalRConfiguration.ClientName } });

                    _hubProxy = _hubConn.CreateHubProxy(SignalRConfiguration.ServiceName);

                    _isInit = true;
                }
            }
        }

        /// <summary>
        /// 开启客户端代理
        /// </summary>
        public void StartProxy()
        {
            Timer.Start();
            Timer.Elapsed += Timer_Elapsed;

            CreateProxy();

            StartListers();

            try
            {
                _hubConn.Start().Wait();
                if (SuccessActionEvent != null)
                    SuccessActionEvent(this, new CometEventArgs("已连接服务."));
            }
            catch (Exception ex)
            {
                if (FailActionEvent != null)
                    FailActionEvent(this, new CometEventArgs("连接服务失败,失败原因:" + ex.Message));
            }
        }

        /// <summary>
        /// 断开后重连
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Timer.Stop();
            if (_hubConn == null) return;
            if (_hubConn.State == ConnectionState.Disconnected) // 重连
            {
                StopProxy();
                _isInit = false;
                StartProxy();
            }
            Timer.Start();
        }

        private void StartListers()
        {
            // 监听消费者
            CurrentProxy.On("ClientHandle", (json) =>
            {
                MessageEnvelope message = JsonConvert.DeserializeObject<MessageEnvelope>(json);
                if (message.ToClientName != SignalRConfiguration.ClientName) return;

                if (string.IsNullOrEmpty(message.Name)) return;
                string name = message.Name.Trim().ToLower();

                ICustomer customer;
                if (!_subscriberInstance.TryGetValue(name, out customer))
                {
                    customer = ServiceLocator.Current.GetInstance<ICustomer>(message.Name.Trim().ToLower());
                    _subscriberInstance.Add(name, customer);
                }
                if (SignalRConfiguration.AsyncSubscribers.Exists(p => p == name))
                    customer.AsyncClientHandle(json);
                else
                {
                    customer.ClientHandle(json);
                }
            });
        }

        public void StopProxy()
        {
            if (_hubConn == null) return;
            _hubConn.Dispose();
        }

        /// <summary>
        /// 代理
        /// </summary>
        public IHubProxy CurrentProxy
        {
            get
            {
                CreateProxy();
                return _hubProxy;
            }
        }

        /// <summary>
        /// 失败通知
        /// </summary>
        public event CometEventHandler<CometEventArgs> FailActionEvent;

        /// <summary>
        /// 成功通知
        /// </summary>
        public event CometEventHandler<CometEventArgs> SuccessActionEvent;

        /// <summary>
        /// 客户端连接ID
        /// </summary>
        public string ConnectionID
        {
            get
            {
                return _hubConn.ConnectionId;
            }
        }
    }
}
